#! /usr/bin/env python
# -*- coding: iso-8859-1 -*-
##############################################################################
#
#
#         ___           ___           ___           ___
#        /  /\         /  /\         /  /\         /  /\
#       /  /::\       /  /:/_       /  /:/_       /  /::\
#      /  /:/\:\     /  /:/ /\     /  /:/ /\     /  /:/\:\
#     /  /:/~/:/    /  /:/ /:/_   /  /:/ /::\   /  /:/~/:/
#    /__/:/ /:/___ /__/:/ /:/ /\ /__/:/ /:/\:\ /__/:/ /:/
#    \  \:\/:::::/ \  \:\/:/ /:/ \  \:\/:/~/:/ \  \:\/:/
#     \  \::/~~~~   \  \::/ /:/   \  \::/ /:/   \  \::/
#      \  \:\        \  \:\/:/     \__\/ /:/     \  \:\
#       \  \:\        \  \::/        /__/:/       \  \:\
#        \__\/         \__\/         \__\/         \__\/
#
#
#
#
#   This file is part of ReSP.
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
#
#   (c) Giovanni Beltrame, Luca Fossati
#       Giovanni.Beltrame@esa.int fossati@elet.polimi.it
#
##############################################################################


import Options
import sys,os

def build(bld):

    # Build and compile the systemc wrapper extension module
    obj = bld.new_task_gen('pypp')

    obj.source ='''
    systemc_export.hpp
    systemc_export.cpp
    '''
    obj.target = 'scwrapper'
    obj.start_decls = 'sc_core sc_dt'
    obj.split = 6
    obj.custom_declaration_code = '''
namespace sandbox { namespace {

    struct sc_module_name_from_python_str{
        sc_module_name_from_python_str(){
            boost::python::converter::registry::push_back(
                &convertible,
                &construct,
                boost::python::type_id<sc_core::sc_module_name>());
        }

        static void* convertible(PyObject* obj_ptr){
            if (PyString_CheckExact(obj_ptr) == 0 && PyString_Check(obj_ptr) == 0){
                PyErr_Clear();
                return 0;
            }
            return obj_ptr;
        }

        static void construct(PyObject* obj_ptr,
                        boost::python::converter::rvalue_from_python_stage1_data* data){
            const char* value = PyString_AsString(obj_ptr);
            if (value == 0){
                boost::python::throw_error_already_set();
            }
            void* storage = (
                        (boost::python::converter::rvalue_from_python_storage<sc_core::sc_module_name>*)
                        data)->storage.bytes;
            new (storage) sc_core::sc_module_name(value);
            data->convertible = storage;
        }
    };


    void init_module(){
        using namespace boost::python;
        sc_module_name_from_python_str();
    }

}} // namespace sandbox::<anonymous>
'''

    obj.custom_registration_code = '''sandbox::init_module();'''

    obj.custom_code = """
# Exclude protected members
mb.global_ns.calldefs( declarations.access_type_matcher_t( 'protected' ) ).exclude()

# Include important stuff
#print "Including classes"
sc_classes = mb.global_ns.classes(lambda decl: decl.name.startswith('sc_') and not 'sc_controller' in decl.name, allow_empty=True)
for i in sc_classes:
    i.ignore = False

sc_enums = mb.global_ns.enumerations(lambda decl: decl.name.startswith('sc_'), allow_empty=True)
for i in sc_enums:
    i.include()

sc_module_name = mb.global_ns.class_('sc_module_name')
sc_module_name.include()

sc_module_decl = mb.global_ns.class_('sc_module')
sc_module_decl.include()
sc_module_decl.member_functions().include()
sc_module_decl.member_functions().set_virtuality( declarations.VIRTUALITY_TYPES.VIRTUAL )
sc_module_decl.member_functions('get_child_objects').set_virtuality( declarations.VIRTUALITY_TYPES.NOT_VIRTUAL )
sc_module_decl.member_functions('sc_get_curr_simcontext').call_policies = call_policies.return_value_policy(call_policies.reference_existing_object)
sc_module_decl.member_operators().exclude()

#print 'including sim_context'
sc_simcontext_decls = mb.global_ns.class_('sc_simcontext')
sc_simcontext_decls.exclude()
sc_simcontext_decls.ignore = False
sc_simcontext_decls.member_function('reset').include()
sc_simcontext_decls.member_function('get_child_objects').include()

#print "Including bind"
bind_decl = mb.global_ns.member_functions( 'bind' )
bind_decl.include()
mb.global_ns.member_functions( 'is_reset' ).call_policies = call_policies.return_value_policy(call_policies.reference_existing_object)

#print "Including Signals"
signal = mb.global_ns.classes(lambda decl: decl.name.startswith('sc_signal<'), allow_empty=True)
signal.include()
for i in signal:
    i.member_functions().exclude()

#print "Including Bit Vectors"
bvectors = mb.global_ns.classes(lambda decl: decl.name.startswith('sc_bv<'), allow_empty=True)
bvectors.include()
for i in bvectors:
    i.member_operators().exclude()

#print "Including clock"
clock = mb.global_ns.class_('sc_clock')
clock.include()
clock.member_functions(lambda decl: decl.name.startswith('period'), allow_empty=True).include()
clock.member_functions(lambda decl: decl.name.startswith('duty_cycle'), allow_empty=True).include()
clock.member_functions(lambda decl: decl.name.startswith('before_end_of_elaboration'), allow_empty=True).exclude()
clock.member_functions(lambda decl: decl.name.startswith('posedge_action'), allow_empty=True).exclude()
clock.member_functions(lambda decl: decl.name.startswith('negedge_action'), allow_empty=True).exclude()
clock.member_functions(lambda decl: decl.name.startswith('report_error'), allow_empty=True).exclude()
clock.member_functions(lambda decl: decl.name.startswith('init'), allow_empty=True).exclude()
clock.member_functions(lambda decl: decl.name.startswith('is_clock'), allow_empty=True).exclude()
clock.member_functions(lambda decl: decl.name.startswith('signal'), allow_empty=True).call_policies = call_policies.return_value_policy(call_policies.reference_existing_object)

#print "Including time"
time = mb.global_ns.class_('sc_time')
time.include()
time.member_functions(lambda decl: decl.name.startswith('print')).exclude()
SC_ZERO_TIME_decl = mb.global_ns.variable('SC_ZERO_TIME')
SC_ZERO_TIME_decl.include()

#print "Including sc_object"
sc_obj = mb.global_ns.class_('sc_object')
sc_obj.include()
sc_obj.member_functions().exclude()
sc_obj.member_functions(lambda decl: decl.name.endswith('name')).include()
sc_obj.member_functions(lambda decl: decl.name.startswith('sc_object')).exclude()
sc_obj.variables(lambda decl: decl.name.startswith('m_name')).exclude()

#print 'Including sc_port'
cls = mb.global_ns.classes(lambda decl: decl.name.startswith('sc_port') , allow_empty=True)
for i in cls:
    i.include()
    for k in i.member_functions(allow_empty=True):
        k.exclude()
    for k in i.member_operators(allow_empty=True):
        k.exclude()

#print 'Including sc_export'
cls = mb.global_ns.classes(lambda decl: decl.name.startswith('sc_export') , allow_empty=True)
for i in cls:
    i.include()
    for k in i.member_functions(allow_empty=True):
        k.exclude()
    for k in i.member_operators(allow_empty=True):
        k.exclude()

#print "Including free functions"
sc_start_decl = mb.global_ns.free_functions('sc_start', allow_empty=True)
for i in sc_start_decl:
    i.include()
sc_stop_decl = mb.global_ns.free_functions('sc_stop', allow_empty=True)
for i in sc_stop_decl:
    i.include()

try:
    my_sc_is_running_decl = mb.global_ns.free_function('my_sc_is_running')
    my_sc_is_running_decl.include()
    my_sc_is_running_decl.alias = 'SYSC_' + my_sc_is_running_decl.alias
except:
    pass

waitFuns = mb.global_ns.free_functions('wait')
for i in waitFuns:
    i.include()
    i.use_overload_macro = True
simContFun = mb.global_ns.free_functions('sc_get_curr_simcontext', allow_empty=True)
for i in simContFun:
    i.include()
    i.call_policies = call_policies.return_value_policy(call_policies.reference_existing_object)

mb.global_ns.calldefs( declarations.access_type_matcher_t('protected') ).exclude()
mb.global_ns.calldefs( declarations.access_type_matcher_t('private') ).exclude()

mb.global_ns.class_('sc_simcontext').member_functions(lambda x: x.name.startswith('clean'), allow_empty=True).include()

sc_names = mb.namespaces(lambda x: x.name in ['sc_core', 'sc_dt'])
for cur_namespace in sc_names:
    for i in cur_namespace.free_functions(allow_empty=True):
        if not i.alias.startswith('SYSC_'):
            i.alias = 'SYSCW_' + i.alias
    for i in cur_namespace.casting_operators(allow_empty=True):
        if not i.alias.startswith('SYSC_'):
            i.alias = 'SYSCW_' + i.alias
    for i in cur_namespace.classes(allow_empty=True):
        if not i.alias.startswith('SYSC_'):
            i.alias = 'SYSCW_' + i.alias
    for i in cur_namespace.enumerations(allow_empty=True):
        if not i.alias.startswith('SYSC_'):
            i.alias = 'SYSCW_' + i.alias
    for i in cur_namespace.variables(allow_empty=True):
        if not i.alias.startswith('SYSC_'):
            i.alias = 'SYSCW_' + i.alias
    for i in cur_namespace.free_operators(allow_empty=True):
        if not i.alias.startswith('SYSC_'):
            i.alias = 'SYSCW_' + i.alias
    for i in cur_namespace.typedefs(allow_empty=True):
        if not i.alias.startswith('SYSC_'):
            i.alias = 'SYSCW_' + i.alias
"""
    obj.uselib = 'SYSTEMC_H TLM BOOST_PYTHON BOOST'
    obj.uselib_local = 'systemc'
    obj.includes = '.'
    obj.templates = [
        'sc_core::sc_signal<char>'
        ,'sc_core::sc_signal<unsigned char>'
        ,'sc_core::sc_signal<int>'
        ,'sc_core::sc_signal<unsigned int>'
        ,'sc_core::sc_signal<long>'
        ,'sc_core::sc_signal<unsigned long>'
        ,'sc_core::sc_signal<bool>'
        ,'sc_core::sc_signal<float>'
        ,'sc_core::sc_signal<double>'
        ,'sc_dt::sc_bv<8>'
        ,'sc_dt::sc_bv<16>'
        ,'sc_dt::sc_bv<32>'
        ,'sc_dt::sc_bv<64>'
        ,'sc_core::sc_port<tlm::tlm_fw_transport_if< tlm::tlm_base_protocol_types>,0,sc_core::SC_ZERO_OR_MORE_BOUND >'
        ,'sc_core::sc_port<tlm::tlm_fw_transport_if< tlm::tlm_base_protocol_types>,1,sc_core::SC_ZERO_OR_MORE_BOUND >'
        ,'sc_core::sc_port<tlm::tlm_fw_transport_if< tlm::tlm_base_protocol_types>,1,sc_core::SC_ONE_OR_MORE_BOUND >'
        ,'sc_core::sc_export<tlm::tlm_fw_transport_if<tlm::tlm_base_protocol_types> >'
    ]
